#!/usr/bin/env python3
"""
git_upload_gui.py

图形界面版的文件上传到多Git仓库工具
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import os
import sys
import threading
import queue
import time
import sqlite3
import tempfile
import shutil
from pathlib import Path
from typing import List, Tuple, Dict, Optional

# 导入原始脚本的功能
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
try:
    # 导入原始脚本中的函数
    from upload_to_multi_git import (
        scan_files, partition_files, init_repo_at, add_and_commit_in_batches,
        push_repo, derive_raw_base, create_github_repo, create_gitee_repo,
        create_gitcode_repo, ensure_branch, save_mappings, DB_SCHEMA
    )
    from git import Repo
    import requests
    from tqdm import tqdm
    
    # 检查依赖是否安装
    DEPENDENCIES_INSTALLED = True
except ImportError as e:
    DEPENDENCIES_INSTALLED = False
    IMPORT_ERROR = str(e)

class GitUploadGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Git多仓库文件上传工具")
        self.root.geometry("900x700")
        
        # 设置中文字体支持
        self.style = ttk.Style()
        if sys.platform == 'win32':
            self.style.configure('.', font=('SimHei', 10))
        
        # 创建消息队列用于线程通信
        self.message_queue = queue.Queue()
        
        # 工作线程
        self.worker_thread = None
        self.is_running = False
        self.stop_event = threading.Event()
        
        # 存储扫描的文件信息
        self.scanned_files = []
        self.file_packs = []
        
        # 创建界面
        self.create_widgets()
        
        # 检查依赖
        if not DEPENDENCIES_INSTALLED:
            messagebox.showerror("依赖错误", f"缺少必要的依赖库:\n{IMPORT_ERROR}\n\n请运行: pip install GitPython requests tqdm")
        
        # 启动消息处理循环
        self.process_messages()
    
    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建配置面板
        config_frame = ttk.LabelFrame(main_frame, text="配置选项", padding="10")
        config_frame.pack(fill=tk.X, pady=5)
        
        # 源目录选择
        ttk.Label(config_frame, text="源目录:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.src_dir_var = tk.StringVar()
        ttk.Entry(config_frame, textvariable=self.src_dir_var, width=70).grid(row=0, column=1, padx=5, pady=5, sticky=tk.EW)
        ttk.Button(config_frame, text="浏览...", command=self.browse_src_dir).grid(row=0, column=2, padx=5, pady=5)
        config_frame.grid_columnconfigure(1, weight=1)
        
        # 仓库设置
        repo_settings_frame = ttk.Frame(config_frame)
        repo_settings_frame.grid(row=1, column=0, columnspan=3, sticky=tk.EW, pady=5)
        
        ttk.Label(repo_settings_frame, text="每个仓库最大容量(MB):").pack(side=tk.LEFT, padx=5)
        self.max_repo_mb_var = tk.StringVar(value="500")
        ttk.Entry(repo_settings_frame, textvariable=self.max_repo_mb_var, width=10).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(repo_settings_frame, text="每个仓库最大文件数(0=不限制):").pack(side=tk.LEFT, padx=5)
        self.max_files_var = tk.StringVar(value="0")
        ttk.Entry(repo_settings_frame, textvariable=self.max_files_var, width=10).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(repo_settings_frame, text="每次提交文件数:").pack(side=tk.LEFT, padx=5)
        self.batch_size_var = tk.StringVar(value="500")
        ttk.Entry(repo_settings_frame, textvariable=self.batch_size_var, width=10).pack(side=tk.LEFT, padx=5)
        
        # 目标子目录
        ttk.Label(config_frame, text="目标子目录:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.target_dir_var = tk.StringVar()
        ttk.Entry(config_frame, textvariable=self.target_dir_var, width=70).grid(row=2, column=1, padx=5, pady=5, sticky=tk.EW)
        
        # 数据库路径
        ttk.Label(config_frame, text="数据库路径:").grid(row=3, column=0, sticky=tk.W, pady=5)
        self.db_path_var = tk.StringVar(value=os.path.join(os.getcwd(), "file_mappings.db"))
        ttk.Entry(config_frame, textvariable=self.db_path_var, width=70).grid(row=3, column=1, padx=5, pady=5, sticky=tk.EW)
        ttk.Button(config_frame, text="浏览...", command=self.browse_db_path).grid(row=3, column=2, padx=5, pady=5)
        
        # 远程仓库设置
        remote_frame = ttk.LabelFrame(config_frame, text="远程仓库设置", padding="10")
        remote_frame.grid(row=4, column=0, columnspan=3, sticky=tk.EW, pady=5)
        
        self.remote_type_var = tk.StringVar(value="existing")
        ttk.Radiobutton(remote_frame, text="使用现有远程仓库", variable=self.remote_type_var, value="existing", command=self.toggle_remote_settings).pack(side=tk.LEFT, padx=10)
        ttk.Radiobutton(remote_frame, text="自动创建远程仓库", variable=self.remote_type_var, value="create", command=self.toggle_remote_settings).pack(side=tk.LEFT, padx=10)
        
        # 现有远程仓库文件
        self.remotes_file_frame = ttk.Frame(remote_frame)
        self.remotes_file_frame.pack(fill=tk.X, pady=5)
        ttk.Label(self.remotes_file_frame, text="远程仓库文件:").pack(side=tk.LEFT, padx=5)
        self.remotes_file_var = tk.StringVar()
        ttk.Entry(self.remotes_file_frame, textvariable=self.remotes_file_var, width=70).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(self.remotes_file_frame, text="浏览...", command=self.browse_remotes_file).pack(side=tk.LEFT, padx=5)
        
        # 自动创建仓库设置
        self.create_repo_frame = ttk.Frame(remote_frame)
        # 初始隐藏
        
        platform_frame = ttk.Frame(self.create_repo_frame)
        platform_frame.pack(fill=tk.X, pady=5)
        ttk.Label(platform_frame, text="平台:").pack(side=tk.LEFT, padx=5)
        self.platform_var = tk.StringVar(value="github")
        ttk.Combobox(platform_frame, textvariable=self.platform_var, values=["github", "gitee", "gitcode"], width=10).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(platform_frame, text="仓库前缀:").pack(side=tk.LEFT, padx=5)
        self.repo_prefix_var = tk.StringVar(value="auto-split")
        ttk.Entry(platform_frame, textvariable=self.repo_prefix_var, width=20).pack(side=tk.LEFT, padx=5)
        
        token_frame = ttk.Frame(self.create_repo_frame)
        token_frame.pack(fill=tk.X, pady=5)
        ttk.Label(token_frame, text="Token:").pack(side=tk.LEFT, padx=5)
        self.token_var = tk.StringVar()
        ttk.Entry(token_frame, textvariable=self.token_var, show="*", width=70).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        # 按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=10)
        
        self.scan_button = ttk.Button(button_frame, text="扫描文件", command=self.start_scan)
        self.scan_button.pack(side=tk.LEFT, padx=5)
        
        self.upload_button = ttk.Button(button_frame, text="开始上传", command=self.start_upload, state=tk.DISABLED)
        self.upload_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(button_frame, text="停止", command=self.stop_upload, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        # 进度条区域
        progress_frame = ttk.Frame(main_frame)
        progress_frame.pack(fill=tk.X, pady=5)
        
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X, padx=5)
        
        self.progress_label = ttk.Label(progress_frame, text="准备就绪")
        self.progress_label.pack(side=tk.LEFT, padx=5)
        
        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="操作日志", padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=15)
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        # 统计信息
        stats_frame = ttk.LabelFrame(main_frame, text="文件统计", padding="10")
        stats_frame.pack(fill=tk.X, pady=5)
        
        self.stats_var = tk.StringVar(value="文件数: 0 | 总大小: 0 MB | 预估仓库数: 0")
        ttk.Label(stats_frame, textvariable=self.stats_var).pack(side=tk.LEFT, padx=5)
    
    def toggle_remote_settings(self):
        if self.remote_type_var.get() == "existing":
            self.remotes_file_frame.pack(fill=tk.X, pady=5)
            self.create_repo_frame.pack_forget()
        else:
            self.remotes_file_frame.pack_forget()
            self.create_repo_frame.pack(fill=tk.X, pady=5)
    
    def browse_src_dir(self):
        dir_path = filedialog.askdirectory(title="选择源目录")
        if dir_path:
            self.src_dir_var.set(dir_path)
    
    def browse_db_path(self):
        file_path = filedialog.asksaveasfilename(title="选择数据库文件", defaultextension=".db", filetypes=[("Database files", "*.db"), ("All files", "*")])
        if file_path:
            self.db_path_var.set(file_path)
    
    def browse_remotes_file(self):
        file_path = filedialog.askopenfilename(title="选择远程仓库文件", filetypes=[("Text files", "*.txt"), ("All files", "*")])
        if file_path:
            self.remotes_file_var.set(file_path)
    
    def log(self, message):
        """添加日志消息到日志区域"""
        self.log_text.insert(tk.END, f"{time.strftime('%H:%M:%S')} - {message}\n")
        self.log_text.see(tk.END)
        
        # 限制日志行数，避免内存占用过高
        line_count = int(self.log_text.index('end-1c').split('.')[0])
        if line_count > 1000:
            self.log_text.delete(1.0, 100.0)
    
    def update_progress(self, value, message):
        """更新进度条和状态消息"""
        self.progress_var.set(value)
        self.progress_label.config(text=message)
    
    def start_scan(self):
        """开始扫描文件"""
        src_dir = self.src_dir_var.get()
        if not src_dir or not os.path.isdir(src_dir):
            messagebox.showerror("错误", "请选择有效的源目录")
            return
        
        # 禁用扫描按钮
        self.scan_button.config(state=tk.DISABLED)
        self.upload_button.config(state=tk.DISABLED)
        
        # 清空之前的扫描结果
        self.scanned_files = []
        self.file_packs = []
        self.stats_var.set("文件数: 0 | 总大小: 0 MB | 预估仓库数: 0")
        
        # 在后台线程中扫描文件
        self.log("开始扫描文件...")
        self.update_progress(0, "扫描文件中...")
        
        # 启动工作线程
        self.stop_event.clear()
        self.worker_thread = threading.Thread(target=self.scan_files_thread)
        self.worker_thread.daemon = True
        self.worker_thread.start()
    
    def scan_files_thread(self):
        """文件扫描线程"""
        try:
            src_dir = self.src_dir_var.get()
            max_repo_mb = int(self.max_repo_mb_var.get())
            max_files = int(self.max_files_var.get()) if int(self.max_files_var.get()) > 0 else None
            
            # 发送进度更新
            self.message_queue.put(("log", "正在扫描目录..."))
            
            # 扫描文件
            total_files = 0
            total_size = 0
            files = []
            root = os.path.abspath(src_dir)
            
            # 获取总文件数用于进度计算
            for _, _, filenames in os.walk(root):
                total_files += len(filenames)
            
            processed_files = 0
            for dirpath, _, filenames in os.walk(root):
                if self.stop_event.is_set():
                    self.message_queue.put(("log", "扫描已取消"))
                    break
                
                for fn in filenames:
                    full = os.path.join(dirpath, fn)
                    try:
                        st = os.stat(full)
                        rel = os.path.relpath(full, root)
                        files.append((rel, st.st_size))
                        total_size += st.st_size
                    except OSError:
                        continue
                    
                    processed_files += 1
                    # 更新进度
                    progress = (processed_files / total_files) * 100 if total_files > 0 else 0
                    self.message_queue.put(("progress", progress, f"扫描文件中... ({processed_files}/{total_files})") if total_files > 0 else ("progress", 0, "扫描文件中..."))
                    
                    # 小延迟，让界面有机会更新
                    if processed_files % 100 == 0:
                        time.sleep(0.01)
            
            if not self.stop_event.is_set():
                # 按大小排序
                files.sort(key=lambda x: x[1], reverse=True)
                
                # 分区
                max_repo_bytes = max_repo_mb * 1024 * 1024
                packs = partition_files(files, max_repo_bytes, max_files)
                
                # 保存结果
                self.scanned_files = files
                self.file_packs = packs
                
                # 更新统计信息
                total_size_mb = total_size / 1024 / 1024
                self.message_queue.put(("stats", len(files), total_size_mb, len(packs)))
                self.message_queue.put(("log", f"扫描完成。找到 {len(files)} 个文件，总大小 {total_size_mb:.2f} MB，将分为 {len(packs)} 个仓库"))
                self.message_queue.put(("button_state", "upload", tk.NORMAL))
        except Exception as e:
            self.message_queue.put(("error", f"扫描过程出错: {str(e)}"))
        finally:
            self.message_queue.put(("progress", 100, "扫描完成"))
            self.message_queue.put(("button_state", "scan", tk.NORMAL))
    
    def start_upload(self):
        """开始上传文件"""
        if not self.scanned_files or not self.file_packs:
            messagebox.showerror("错误", "请先扫描文件")
            return
        
        # 验证远程仓库设置
        if self.remote_type_var.get() == "existing":
            remotes_file = self.remotes_file_var.get()
            if not remotes_file or not os.path.isfile(remotes_file):
                messagebox.showerror("错误", "请选择有效的远程仓库文件")
                return
        else:
            token = self.token_var.get()
            if not token:
                messagebox.showerror("错误", "请输入有效的Token")
                return
        
        # 禁用相关按钮
        self.scan_button.config(state=tk.DISABLED)
        self.upload_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        
        # 启动工作线程
        self.stop_event.clear()
        self.worker_thread = threading.Thread(target=self.upload_files_thread)
        self.worker_thread.daemon = True
        self.worker_thread.start()
    
    def upload_files_thread(self):
        """文件上传线程"""
        try:
            # 获取配置
            src_dir = self.src_dir_var.get()
            max_repo_mb = int(self.max_repo_mb_var.get())
            batch_size = int(self.batch_size_var.get())
            db_path = self.db_path_var.get()
            target_dir = self.target_dir_var.get()
            
            # 远程仓库设置
            remotes = []
            remote_urls = []
            
            if self.remote_type_var.get() == "existing":
                remotes_file = self.remotes_file_var.get()
                with open(remotes_file, 'r', encoding='utf-8') as f:
                    remotes = [line.strip() for line in f if line.strip()]
            else:
                platform = self.platform_var.get()
                token = self.token_var.get()
                repo_prefix = self.repo_prefix_var.get()
            
            # 创建工作目录
            work_root = tempfile.mkdtemp(prefix='multi_git_')
            self.message_queue.put(("log", f"工作目录: {work_root}"))
            
            # 处理每个文件包
            total_packs = len(self.file_packs)
            for idx, pack in enumerate(self.file_packs):
                if self.stop_event.is_set():
                    self.message_queue.put(("log", "上传已取消"))
                    break
                
                # 计算整体进度
                pack_progress = (idx / total_packs) * 100
                self.message_queue.put(("progress", pack_progress, f"处理仓库 {idx+1}/{total_packs}"))
                
                # 创建仓库
                name = f"{self.repo_prefix_var.get()}-{idx+1}" if self.remote_type_var.get() == "create" else f"auto-split-{idx+1}"
                repo_path = os.path.join(work_root, name)
                os.makedirs(repo_path, exist_ok=True)
                
                # 日志
                rels = [rel for rel, _ in pack]
                pack_size = sum(s for _, s in pack) / 1024 / 1024
                self.message_queue.put(("log", f"处理仓库 {idx+1}/{total_packs}: {len(rels)} 个文件, 大小 {pack_size:.2f} MB"))
                
                # 初始化仓库
                repo = init_repo_at(repo_path)
                ensure_branch(repo, 'main')
                
                # 逐批复制并 commit，传入目标子目录
                total_batches = (len(rels) + batch_size - 1) // batch_size
                self.message_queue.put(("log", f"开始提交文件，共 {total_batches} 批"))
                
                for batch_idx in range(0, len(rels), batch_size):
                    if self.stop_event.is_set():
                        break
                    
                    batch = rels[batch_idx:batch_idx+batch_size]
                    batch_progress = ((batch_idx / len(rels)) * 100 + pack_progress) / 2  # 分配进度权重
                    self.message_queue.put(("progress", batch_progress, f"提交文件批 {batch_idx//batch_size + 1}/{total_batches}"))
                    
                    # 复制文件
                    wdir = Path(repo_path)
                    for rel in batch:
                        if self.stop_event.is_set():
                            break
                        
                        src = os.path.join(src_dir, rel)
                        # 目标路径处理
                        if target_dir:
                            dst_rel_path = os.path.join(target_dir, rel)
                        else:
                            dst_rel_path = rel
                        
                        dst = wdir.joinpath(dst_rel_path)
                        dst.parent.mkdir(parents=True, exist_ok=True)
                        if os.path.exists(src):
                            shutil.copy2(src, dst)
                    
                    if self.stop_event.is_set():
                        break
                    
                    # 添加到git
                    files_to_add = []
                    for rel in batch:
                        if target_dir:
                            add_path = os.path.join(target_dir, rel)
                        else:
                            add_path = rel
                        add_path = add_path.replace('\\', '/')
                        files_to_add.append(add_path)
                    
                    repo.index.add(files_to_add)
                    msg = f"Add batch {batch_idx//batch_size + 1} files {len(batch)}"
                    repo.index.commit(msg)
                    
                    # 小延迟，减少CPU占用
                    time.sleep(0.01)
                
                if self.stop_event.is_set():
                    break
                
                # 处理远程仓库
                remote_url = None
                raw_base = None
                
                if self.remote_type_var.get() == "existing":
                    if remotes and idx < len(remotes):
                        remote_url = remotes[idx]
                else:
                    # 自动创建仓库
                    try:
                        platform = self.platform_var.get()
                        token = self.token_var.get()
                        
                        if platform == 'github':
                            remote_url = create_github_repo(token, name)
                        elif platform == 'gitee':
                            remote_url = create_gitee_repo(token, name)
                        elif platform == 'gitcode':
                            remote_url = create_gitcode_repo(token, name)
                    except Exception as e:
                        self.message_queue.put(("log", f"创建远程仓库失败: {str(e)}"))
                
                # 推送仓库
                if remote_url:
                    self.message_queue.put(("log", f"开始推送仓库到 {remote_url}"))
                    push_progress = pack_progress + 50  # 推送占50%权重
                    self.message_queue.put(("progress", push_progress, f"推送仓库 {idx+1}/{total_packs}"))
                    
                    try:
                        push_repo(repo, remote_url, 'main', batch_size=5)  # 可以根据需要调整批次大小
                        
                        self.message_queue.put(("log", f"推送成功"))
                        
                        # 推送后推断 raw 地址
                        raw_base = derive_raw_base(remote_url, 'main', target_dir)
                    except Exception as e:
                        self.message_queue.put(("log", f"推送失败: {str(e)}"))
                
                # 保存映射到数据库
                repo_info = {'name': name, 'remote': remote_url, 'branch': 'main'}
                save_mappings(db_path, repo_info, rels, raw_base, target_dir)
                self.message_queue.put(("log", f"已保存仓库 {name} 的文件映射到数据库"))
            
            if not self.stop_event.is_set():
                self.message_queue.put(("log", "所有仓库处理完成！"))
                self.message_queue.put(("log", f"数据库位置: {db_path}"))
                self.message_queue.put(("progress", 100, "上传完成"))
            else:
                self.message_queue.put(("log", "上传已取消"))
                self.message_queue.put(("progress", 0, "已取消"))
        except Exception as e:
            self.message_queue.put(("error", f"上传过程出错: {str(e)}"))
            self.message_queue.put(("progress", 0, "上传失败"))
        finally:
            # 清理工作目录
            try:
                if 'work_root' in locals() and os.path.exists(work_root):
                    shutil.rmtree(work_root)
                    self.message_queue.put(("log", f"已清理工作目录: {work_root}"))
            except:
                pass
            
            # 恢复按钮状态
            self.message_queue.put(("button_state", "scan", tk.NORMAL))
            self.message_queue.put(("button_state", "upload", tk.NORMAL))
            self.message_queue.put(("button_state", "stop", tk.DISABLED))
    
    def stop_upload(self):
        """停止上传操作"""
        if self.worker_thread and self.worker_thread.is_alive():
            self.stop_event.set()
            self.log("正在停止操作...")
    
    def process_messages(self):
        """处理来自工作线程的消息"""
        try:
            while not self.message_queue.empty():
                message = self.message_queue.get_nowait()
                
                if message[0] == "log":
                    self.log(message[1])
                elif message[0] == "progress":
                    self.update_progress(message[1], message[2])
                elif message[0] == "error":
                    self.log(message[1])
                    messagebox.showerror("错误", message[1])
                elif message[0] == "button_state":
                    if message[1] == "scan":
                        self.scan_button.config(state=message[2])
                    elif message[1] == "upload":
                        self.upload_button.config(state=message[2])
                    elif message[1] == "stop":
                        self.stop_button.config(state=message[2])
                elif message[0] == "stats":
                    self.stats_var.set(f"文件数: {message[1]} | 总大小: {message[2]:.2f} MB | 预估仓库数: {message[3]}")
        except queue.Empty:
            pass
        
        # 继续处理消息
        self.root.after(100, self.process_messages)

if __name__ == "__main__":
    root = tk.Tk()
    app = GitUploadGUI(root)
    root.mainloop()